package com.after.saas.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.after.saas.gateway.config.RequestListConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.after.saas.common.constants.Constants;
import com.after.saas.common.constants.ErrorCodeEnum;
import com.after.saas.common.constants.JwtEnum;
import com.after.saas.common.constants.Result;
import com.after.saas.common.exception.BizException;
import com.after.saas.common.utils.JwtUtil;
import com.after.saas.common.utils.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @Classname AuthFilter
 * @Description TODO
 * @Date 2021/5/25 14:03
 * @Created by hjchen
 */

@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {


    @Value("${jwt.encode.secret}")
    private String SECRET;

    @Autowired
    private RequestListConfig freeListConfig;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        //如果是登录，则放行
        List<String> freeList = freeListConfig.getFreeList();
        //不需要校验静态资源
        freeList.addAll(freeListConfig.getStaticList());
        String requestPath = request.getURI().getPath();
        if (freeListConfig.isMatched(freeList, requestPath)) {
            return chain.filter(exchange);
        }
        try {
            parseToken(request);
        } catch (Exception e) {
            e.printStackTrace();
            if (e instanceof BizException) {
                return responseError(request, exchange, ResponseUtil.FORBID(e.getMessage()));
            } else {
                return responseError(request, exchange, ResponseUtil.FAILURE(ErrorCodeEnum.INNER_ERROE.getErrorMsg()));
            }
        }
        return chain.filter(exchange);
    }

    /**
     * 解析并校验token，校验成功后将userId放到data中返回
     *
     * @param request
     * @return
     */
    private void parseToken(ServerHttpRequest request) {
        JwtEnum jwtEnum = JwtEnum.WEB_FRONT;
        String loginToken = getLoginToken(request, jwtEnum.getTokenName());
        if (StrUtil.isBlank(loginToken)) {
            throw new BizException(ErrorCodeEnum.ACCESS_TOKEN_EMPTY);
        }
        String userId = JwtUtil.getUserIdFromToken(loginToken, SECRET);
        if (userId.isEmpty()) {
            throw new BizException(ErrorCodeEnum.ACCESS_TOKEN_INVALID);
        }
        if (!JwtUtil.isTokenExpired(loginToken, SECRET)) {
            throw new BizException(ErrorCodeEnum.ACCESS_TOKEN_EXPIRE);
        }
    }

    /**
     * 获取登录token
     *
     * @param request
     * @return
     */
    private String getLoginToken(ServerHttpRequest request, String tokenName) {
        String loginToken = null;
        HttpHeaders httpHeaders = request.getHeaders();
        if (httpHeaders.containsKey(tokenName)) {
            loginToken = httpHeaders.get(tokenName).get(0);
        } else if (request.getMethod().equals(HttpMethod.GET)) {
            if (loginToken == null) {
                MultiValueMap<String, String> multiValueMap = request.getQueryParams();
                if (multiValueMap.containsKey(tokenName)) {
                    loginToken = multiValueMap.get(tokenName).get(0);
                }
            }
        }
        if (StrUtil.isNotEmpty(loginToken) && loginToken.startsWith(Constants.TOKEN_PREFIX)) {
            loginToken = loginToken.substring(Constants.TOKEN_PREFIX.length());
        }
        return loginToken;
    }

    /**
     * 输出异常信息
     *
     * @param request
     * @param exchange
     * @param responseEntity
     * @return
     * @throws Exception
     */
    private Mono responseError(ServerHttpRequest request, ServerWebExchange exchange, Result responseEntity) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.OK);
        //指定编码格式UTF-8,解决中文乱码问题
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        log.error("请求{},发生异常，{}", request.getURI(), responseEntity);
        byte[] responseBytes = new byte[0];
        try {
            responseBytes = objectMapper.writeValueAsBytes(responseEntity);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        //c++转发只支持Content-Length
        response.getHeaders().add("Content-Length", responseBytes.length + "");
        return response.writeWith(Mono.just(response.bufferFactory().wrap(responseBytes)));
    }

    @Override
    public int getOrder() {
        return -100;
    }

}